package pokerantani.v2;

import java.io.BufferedReader;
import java.io.IOException;
import java.util.List;

import pokerantani.v2.linematchers.*;
import pokerantani.v2.model.*;
import pokerantani.v2.model.actions.*;

public class HandParser {
	private GamePhase phase = GamePhase.INTRO;
	private final BufferedReader lineReader;
	public final Hand hand = new Hand();
	
	public HandParser(BufferedReader lineReader) throws IOException {
		this.lineReader = lineReader;

		String line = readLine();
		int match = 0;
		
		if(!header.match(line))
			throw new IllegalArgumentException("Invalid format: " + line);
		
		int buttonPlayerNum = -1;
		
		// loop lines:
		while((line = readLine()) != null) {
			match = 0;
			
			if(uselessLine.match(line)) match++; //continue;
			
			// actions matched in (almost) every phase:
			List<Action> target = hand.actions.get(phase);
			if(actCheck.match(line)) {
				target.add(new Check(getPlayer(actCheck.playerName)));
				match++;
			}
			if(actBet.match(line)) {
				target.add(new Bet(getPlayer(actBet.playerName), actBet.amount));
				match++;
			}
			if(actCall.match(line)) {
				target.add(new Call(getPlayer(actCall.playerName), actCall.allin));
				match++;
			}
			if(actRaise.match(line)) {
				target.add(new Raise(getPlayer(actRaise.playerName), actRaise.from, actRaise.to, actRaise.allin));
				match++;
			}
			if(uncalledBetReturned.match(line)) {
				target.add(new ReturnBet(getPlayer(uncalledBetReturned.playerName), uncalledBetReturned.amount));
				match++;
			}
			if(actChat.match(line)) {
				// ignore chat
				match++;
			}
			
			// phase-specific matchers:
			switch(phase) {
			case INTRO:
				if(tableInfo.match(line)) {
					buttonPlayerNum = tableInfo.button;
					match++;
				}
				if(seatInfo.match(line)) {
					Player p = new Player(seatInfo.seatNumber, seatInfo.playerName, seatInfo.playerChips);
					hand.players.add(p);
					if(seatInfo.seatNumber == buttonPlayerNum) hand.buttonPlayer = p;
					match++;
				}
				if(buttonSeat.match(line)) {
					for(Player p : hand.players) {
						if(p.seatNumber == buttonSeat.seatNumber) {
							hand.buttonPlayer = p;
							break;
						}
					}
					match++;
				}
				if(postBlind.match(line)) {
					if(postBlind.small) {
						hand.smallBlind = postBlind.amount;
						hand.smallBlindPlayer = getPlayer(postBlind.playerName);
					} else {
						hand.bigBlind = postBlind.amount;
						hand.bigBlindPlayer = getPlayer(postBlind.playerName);
					}
					match++;
				}
				if(phaseMark.match(line)) {
					if(phaseMark.phase != GamePhase.PREFLOP)
						throw new RuntimeException("Unexpected phase: " + phaseMark.phase + ": " + line);
					
					// move Hero to 1st position:
					Player hero = getPlayer("Hero");
					if(hero == null)
						throw new RuntimeException("Cannot find Hero in the players list. Current players list: " + hand.players);
					hand.players.remove(hero);
					hand.players.add(0, hero);
					
					if(hand.buttonPlayer == null)
						throw new RuntimeException("Cannot determine button player. Players list: " + hand.players + ", buttonPlayer seat: " + buttonPlayerNum);
					
					phase = GamePhase.PREFLOP;
					match++;
				}
				break;
			case PREFLOP:
				if(heroGotCards.match(line)) {
					hand.players.get(0).cards.addAll(heroGotCards.cards);
					match++;
				}
				if(phaseMark.match(line)) {
					if(phaseMark.phase != GamePhase.FLOP)
						throw new RuntimeException("Unexpected phase: " + phaseMark.phase + ": " + line);
					
					hand.flopCards.addAll(phaseMark.cards);
					phase = GamePhase.FLOP;
					match++;
				}
				break;
			case FLOP:
				if(phaseMark.match(line)) {
					if(phaseMark.phase != GamePhase.TURN)
						throw new RuntimeException("Unexpected phase: " + phaseMark.phase + ": " + line);
					
					hand.turnCard = phaseMark.cards.get(0);
					phase = GamePhase.TURN;
					match++;
				}
				break;
			case TURN:
				if(phaseMark.match(line)) {
					if(phaseMark.phase != GamePhase.RIVER)
						throw new RuntimeException("Unexpected phase: " + phaseMark.phase + ": " + line);
					
					hand.riverCard = phaseMark.cards.get(0);
					phase = GamePhase.RIVER;
					match++;
				}
				break;
			case RIVER:
				if(phaseMark.match(line)) {
					if(phaseMark.phase != GamePhase.SHOWDOWN)
						throw new RuntimeException("Unexpected phase: " + phaseMark.phase + ": " + line);
					
					phase = GamePhase.SHOWDOWN;
					match++;
				}
				break;
			case SHOWDOWN:
				if(actShowdown.match(line)) {
					Player player = getPlayer(actShowdown.playerName);
					if(player.cards.size() == 0)
						player.cards.addAll(actShowdown.cards);
					match++;
				}
				if(collectFromPot.match(line)) {
					// TODO: or ignore ?
					match++;
				}
				if(phaseMark.match(line)) {
					if(phaseMark.phase != GamePhase.SUMMARY)
						throw new RuntimeException("Unexpected phase: " + phaseMark.phase + ": " + line);
					phase = GamePhase.SUMMARY;
					match++;
				}
				break;
			case SUMMARY:
				if(totalPot.match(line)) {
					// TODO: or ignore ?
					match++;
				}
				if(boardSummary.match(line)) {
					// TODO: or ignore ?
					match++;
				}
				if(actionSummary.match(line)) {
					// TODO: or ignore ?
					match++;
				}
				break;
			}
			if(match == 0)
				throw new RuntimeException("UNMATCHED LINE: " + line);
		}
	}
	
	protected String readLine() throws IOException {
		String line = lineReader.readLine();
		if(line == null) return null;
		
		// trim initial and trailing spaces
		line = line.trim();
		if(System.getProperty("debug", "0").equals("1"))
			System.out.println("LINE: " + line);
		return line;
	}
	
	protected Player getPlayer(String playerName) {
		int idx = hand.players.indexOf(playerName);
		return idx >= 0 ? hand.players.get(idx) : null;
	}
	
	ActBet actBet = new ActBet();
	ActCall actCall = new ActCall();
	ActChat actChat = new ActChat();
	ActCheck actCheck = new ActCheck();
	ActionSummary actionSummary = new ActionSummary();
	ActRaise actRaise = new ActRaise();
	ActShowdown actShowdown = new ActShowdown();
	BoardSummary boardSummary = new BoardSummary();
	ButtonSeat buttonSeat = new ButtonSeat();
	CollectFromPot collectFromPot = new CollectFromPot();
	Header header = new Header();
	HeroGotCards heroGotCards = new HeroGotCards();
	PhaseMark phaseMark = new PhaseMark();
	PostBlind postBlind = new PostBlind();
	SeatInfo seatInfo = new SeatInfo();
	TableInfo tableInfo = new TableInfo();
	TotalPot totalPot = new TotalPot();
	UncalledBetReturned uncalledBetReturned = new UncalledBetReturned();
	UselessLine uselessLine = new UselessLine();
}
